﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.Linq;
using System.ServiceModel;
using MEFedMVVM.ViewModelLocator;
using CRM.Tools.ODataQueryDesigner.CrmSdk;


namespace CRM.Tools.ODataQueryDesigner.Readers
{
    [ExportService(ServiceType.Runtime, typeof(IMetadataReader))]
    public class MetadataReader : IMetadataReader
    {


        #region private variables
        
        private ObservableCollection<EntityClass> _entities;
        private EntityClass _entity;
        
        #endregion


        #region contructors

        /// <summary>
        /// Constructor
        /// </summary>
        public MetadataReader()
        {
            _entities = new ObservableCollection<EntityClass>();
            _entity = new EntityClass("", "");
        }

        #endregion


        #region IMetadataReader Members

        /// <summary>
        /// 
        /// </summary>
        /// <param name="exceptionMessages"></param>
        /// <param name="exceptionHandler"></param>
        /// <returns></returns>
        public ObservableCollection<EntityClass> GetEntities()
        {
            try
            {

                _entities = new ObservableCollection<EntityClass>(); 

                Uri serverUrl = new Uri(ClientScript.GetServerUrl());

                ExecuteRetrieveEntities(serverUrl);

                return _entities;
            
            }
            catch (FaultException<OrganizationServiceFault> ex)
            {
                throw ex;
            }

        }

        public EntityClass GetEntity(String LogicalName, bool IncludeRelationships)
        {
            try
            {
                _entity = new EntityClass("", LogicalName);
               
                Uri serverUrl = new Uri(ClientScript.GetServerUrl());

                ExecuteRetrieveEntity(LogicalName, IncludeRelationships, serverUrl);

                return _entity;

            }
            catch (FaultException<OrganizationServiceFault> ex)
            {
                throw ex;
            }
        }


        #endregion


        #region private methods


        /// <summary>
        /// Retrieves 
        /// </summary>
        /// <param name=ServerUrl">ServerUrl</param>
        private void ExecuteRetrieveEntities(Uri ServerUrl)
        {
            try
            {
                OrganizationServiceClient client = (OrganizationServiceClient)WCFHelper.GetConnection(ServerUrl);
               
                OrganizationRequest request = new OrganizationRequest();
                request.RequestName = "RetrieveAllEntities";
                request.Parameters = new ParameterCollection();
                request.Parameters.Add(new CRM.Tools.ODataQueryDesigner.DataModel.KeyValuePair<string, object>("EntityFilters", EntityFilters.Entity));
                request.Parameters.Add(new CRM.Tools.ODataQueryDesigner.DataModel.KeyValuePair<string, object>("RetrieveAsIfPublished", false));
                client.ExecuteCompleted += new EventHandler<CrmSdk.ExecuteCompletedEventArgs>(ExecuteRetrieveEntities_ExecuteCompleted);

                client.ExecuteAsync(request);
                
            }
            catch (FaultException<OrganizationServiceFault>  ex)
            {
                throw ex;
            }
        
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="response"></param>
        private void ExecuteRetrieveEntities_ExecuteCompleted(object sender, CrmSdk.ExecuteCompletedEventArgs response)
        {
            try
            {
                ObservableCollection<EntityMetadata> data = (ObservableCollection<EntityMetadata>)response.Result.Results[0].Value;

                var entities = from a in data
                               orderby a.SchemaName
                               select new {SchemaName = a.SchemaName, LogicalName = a.LogicalName};

                foreach (var entity in entities)
                {
                    _entities.Add(new EntityClass(entity.SchemaName + "Set" , entity.LogicalName));
                }

            }

            catch (FaultException<OrganizationServiceFault> se)
            {
                throw se;
            }
        }


        /// <summary>
        /// Retrieves 
        /// </summary>
        /// <param name=ServerUrl">ServerUrl</param>
        private void ExecuteRetrieveEntity(String EntityLogicalName,bool IncludeRelationships, Uri ServerUrl)
        {
            try
            {
                OrganizationServiceClient client = (OrganizationServiceClient)WCFHelper.GetConnection(ServerUrl);

                OrganizationRequest request = new OrganizationRequest();
                request.RequestName = "RetrieveEntity";
                request.Parameters = new ParameterCollection();
                request.Parameters.Add(new CRM.Tools.ODataQueryDesigner.DataModel.KeyValuePair<string, object>("LogicalName", EntityLogicalName));
                if(IncludeRelationships)
                    request.Parameters.Add(new CRM.Tools.ODataQueryDesigner.DataModel.KeyValuePair<string, object>("EntityFilters", EntityFilters.Attributes | EntityFilters.Relationships));
                else
                    request.Parameters.Add(new CRM.Tools.ODataQueryDesigner.DataModel.KeyValuePair<string, object>("EntityFilters", EntityFilters.Attributes));

                request.Parameters.Add(new CRM.Tools.ODataQueryDesigner.DataModel.KeyValuePair<string, object>("MetadataId", new Guid("00000000-0000-0000-0000-000000000000")));
                request.Parameters.Add(new CRM.Tools.ODataQueryDesigner.DataModel.KeyValuePair<string, object>("RetrieveAsIfPublished", false));
                client.ExecuteCompleted += new EventHandler<CrmSdk.ExecuteCompletedEventArgs>(ExecuteRetrieveEntity_ExecuteCompleted);

                client.ExecuteAsync(request);

            }
            catch (FaultException<OrganizationServiceFault> ex)
            {
                throw ex;
            }

        }

        /// <summary>
        /// Handles the post back for retrieveing an entity
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="response"></param>
        private void ExecuteRetrieveEntity_ExecuteCompleted(object sender, CrmSdk.ExecuteCompletedEventArgs response)
        {
            try
            {
                EntityClass entityTmp = _entity;
               
                EntityMetadata data = (EntityMetadata)response.Result.Results[0].Value;

                if (data.Attributes != null)
                {

                    entityTmp.Attributes.Add(new AttributeClass(new AttributeMetadata() { SchemaName = "(Select All)" }));
                    
                    var attributes = from a in data.Attributes
                                     orderby a.SchemaName
                                     where a.AttributeOf == null
                                     select a;
                    
                    foreach (var attribute in attributes)
                    {
                            entityTmp.Attributes.Add(new AttributeClass(attribute));
                            entityTmp.SortByAttributes.Add(new AttributeClass(attribute));
                            entityTmp.FilterAttributes.Add(new AttributeClass(attribute));
                    }

                    //var attributes = from a in data.Attributes
                    //                 orderby a.SchemaName
                    //                 where a.AttributeOf == null
                    //                 select new { SchemaName = a.SchemaName, AttributeType = a.AttributeType, EntityLogicalName = a.EntityLogicalName };

                    //entityTmp.Attributes.Add(new AttributeClass("(Select All)", null, null));

                    
                    //foreach (var attribute in attributes)
                    //{
                    //    //((BooleanAttributeMetadata) attribute).
                    //    entityTmp.Attributes.Add(new AttributeClass(attribute.SchemaName, attribute.AttributeType, attribute.EntityLogicalName));
                    //    entityTmp.SortByAttributes.Add(new AttributeClass(attribute.SchemaName, attribute.AttributeType, attribute.EntityLogicalName));
                    //    entityTmp.FilterAttributes.Add(new AttributeClass(attribute.SchemaName, attribute.AttributeType, attribute.EntityLogicalName));
                    //}

                    //
                    // Add first filter
                    if (entityTmp.FilterAttributes.Count > 0)
                    {
                        FilterClass filter = new FilterClass();
                        filter.Attributes = new AttributeClass[entityTmp.FilterAttributes.Count];
                        entityTmp.FilterAttributes.CopyTo(filter.Attributes, 0);
                        entityTmp.Filters.Add(filter);
                    }

                }

                if (data.OneToManyRelationships != null)
                {
                    var oneToManys = from a in data.OneToManyRelationships
                                     orderby a.SchemaName
                                     where a.IsValidForAdvancedFind.Value.Equals(true)
                                     select new { SchemaName = a.SchemaName, EntityLogicalName = a.ReferencingEntity };

                    foreach (var oneToMany in oneToManys)
                    {
                        entityTmp.Relationships_OneToMany.Add(new RelationshipClass(oneToMany.SchemaName, oneToMany.EntityLogicalName));

                    }
                }

                if (data.ManyToOneRelationships != null)
                {
                    var manyToOnes = from a in data.ManyToOneRelationships
                                     orderby a.SchemaName
                                     where a.IsValidForAdvancedFind.Value.Equals(true)
                                     select new { SchemaName = a.SchemaName, EntityLogicalName = a.ReferencedEntity };

                    foreach (var manyToOne in manyToOnes)
                    {
                        entityTmp.Relationships_ManyToOne.Add(new RelationshipClass(manyToOne.SchemaName, manyToOne.EntityLogicalName));
                    }

                }

                if (data.ManyToManyRelationships != null)
                {
                    var manyToManys = from a in data.ManyToManyRelationships
                                      orderby a.SchemaName
                                      where a.IsValidForAdvancedFind.Value.Equals(true)
                                      select new { SchemaName = a.SchemaName, EntityLogicalName = a.Entity2LogicalName };

                    foreach (var manyToMany in manyToManys)
                    {
                        entityTmp.Relationships_ManyToMany.Add(new RelationshipClass(manyToMany.SchemaName, manyToMany.EntityLogicalName));
                    }
                }          

                _entity = entityTmp;
  
            }

            catch (FaultException<OrganizationServiceFault> se)
            {
                throw se;
            }
        }


        #endregion

    }
}
